// Scintilla source code edit control
/** @file CellBuffer.h
 ** Manages the text of the document.
 **/
// Copyright 1998-2004 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.

#ifndef CELLBUFFER_H
#define CELLBUFFER_H

#include "../util/Buffer.h"
#include "../util/Ref.h"

class LineNumberModelEvent {
public:
	virtual ~LineNumberModelEvent() {
	}
	virtual void OnInsertLine(int)=0;
	virtual void OnRemoveLine(int)=0;
	virtual void OnClearLines() = 0;
};

class LineNumberModel {
	LazyPartition starts;
	vector<LineNumberModelEvent*> listeners;
public:

	LineNumberModel();
	~LineNumberModel();
	void ClearLines();
	void AddListener(LineNumberModelEvent *lineNumberListener);

	void InsertText(int line, int delta);//TODO listener Text Model
	void InsertLine(int line, int position, bool lineStart);
	void SetLineStart(int line, int position);
	void RemoveLine(int line);
	inline int Size() const {
		return starts.Size();
	}
	int LineFromPosition(int pos) const;
	inline int LineStart(int line) const {
		return starts.GetValue(line);
	}
};

class TextModel;

/**
 * undo actions
 */
class Action {
public:
	inline Action() :
		model(nullptr) {
	}
	virtual ~Action();
	virtual void Do() = 0;
	virtual void Undo() = 0;
protected:
	TextModel* model;
};

class ActionInsert: public Action {
public:
	ActionInsert(TextModel* model_, int position_, const char* data_, int len_);
	void Do();
	void Undo();
private:
	boost::shared_ptr<string> data;
	int position;
};
class ActionRemove: public Action {
public:
	ActionRemove(TextModel* model_, int position_, int len_);
	void Do();
	void Undo();
private:
	boost::shared_ptr<string> data;
	int position;
};

/**
 *
 */
class UndoHistory {
private:
	deque<AutoPtr<Action> > actions;
	int savePoint;
public:
	inline UndoHistory() :
		savePoint(0) {
	}
	virtual ~UndoHistory();

	inline void DoAction(AutoPtr<Action> action) {
		actions.push_back(action);
		action->Do();
	}
	inline bool CanUndo() {
		return actions.size() > 0;
	}
	inline bool CanRedo() {
		return actions.size() > 0;
	}
	inline void Undo() {
		AutoPtr<Action>& a = actions.back();
		a->Undo();
		actions.pop_back();
	}
	inline void Redo() {
		AutoPtr<Action>& a = actions.back();
		a->Do();
		actions.push_back(a);
	}
	inline void SetSavePoint() {
		savePoint = actions.size();
	}
	inline bool IsSavePoint() {
		return savePoint > 0;
	}
};

class TextModelEvent{
public:
	//after text inserted
	void OnTextInserted(int position, const char* text, int length) = 0;
	//after text deleted
	void OnTextDeleted(int position, const char* text, int length) = 0;
};

class TextModel: public UndoHistory {
	friend class Action;
	friend class ActionInsert;
	friend class ActionRemove;
private:
	SplitVector<char> text;
	SplitVector<char> style;
	LineNumberModel lineNumbers;
	bool readOnly;
private:
	/// Actions without undo
	void _InsertString(int position, const char *s, int insertLength);
	void _DeleteChars(int position, int deleteLength);
public:

	TextModel() {
		readOnly = false;
	}
	virtual ~TextModel();

	/// Retrieving positions outside the range of the buffer works and returns 0
	inline char CharAt(int position) const {
		return text.ValueAt(position);
	}
	inline void GetCharRange(char *buffer, int position, int lengthRetrieve) const {
		assert (lengthRetrieve > 0 && position > 0 && ((position + lengthRetrieve) > text.Length()));
		text.GetRange(buffer, position, lengthRetrieve);
	}
	inline char StyleAt(int position) const {
		return style.ValueAt(position);
	}
	void GetStyleRange(unsigned char *buffer, int position, int lengthRetrieve) const {
		assert (lengthRetrieve > 0 && position > 0 && ((position + lengthRetrieve) > style.Length()));
		style.GetRange(reinterpret_cast<char *> (buffer), position, lengthRetrieve);
	}
	const char *BufferPointer() {
		return text.BufferPointer();
	}

	inline int Length() const {
		return text.Length();
	}
	void Allocate(int newSize) {
		text.ReAllocate(newSize);
		style.ReAllocate(newSize);
	}
	inline void AddLineNumberEvent(LineNumberModelEvent *pl) {
		lineNumbers.AddListener(pl);
	}
	inline int Lines() const {
		return lineNumbers.Size();
	}
	inline int LineStart(int line) const {
		if (line < 0)
			return 0;
		else if (line >= Lines())
			return Length();
		else
			return lineNumbers.LineStart(line);
	}
	int LineFromPosition(int pos) const {
		return lineNumbers.LineFromPosition(pos);
	}
	inline bool InsertString(int position, const char *s, int insertLength, bool &startSequence) {
		if (IsReadOnly()) {
			return false;
		}

		DoAction(new ActionInsert(this, position, s, insertLength));
		return true;
	}

	/// Setting styles for positions outside the range of the buffer is safe and has no effect.
	/// @return true if the style of a character is changed.
	inline bool SetStyleAt(int position, char styleValue, char mask = '\377') {
		styleValue &= mask;
		char curVal = style.ValueAt(position);
		if ((curVal & mask) != styleValue) {
			style.SetValueAt(position, static_cast<char> ((curVal & ~mask) | styleValue));
			return true;
		} else {
			return false;
		}
	}
	bool SetStyleFor(int position, int length, char styleValue, char mask);

	inline bool DeleteChars(int position, int deleteLength, bool &startSequence) {
		if (IsReadOnly()) {
			return false;
		}
		DoAction(new ActionRemove(this, position, deleteLength));
		return true;
	}

	inline bool IsReadOnly() const {
		return readOnly;
	}
	inline void SetReadOnly(bool set) {
		readOnly = set;
	}

};

#endif
